package com.yzh.dbrouter.strategy.impl;

import com.yzh.dbrouter.DBContextHolder;
import com.yzh.dbrouter.DBRouterConfig;
import com.yzh.dbrouter.strategy.IDBRouterStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 哈希路由
 */
public class DBRouterStrategyImpl implements IDBRouterStrategy {
    private Logger logger = LoggerFactory.getLogger(DBRouterStrategyImpl.class);
    private DBRouterConfig dbRouterConfig;

    public DBRouterStrategyImpl(DBRouterConfig dbRouterConfig) {
        this.dbRouterConfig = dbRouterConfig;
    }

    /**
     * 路由
     * 1.仅支持2的幂次长度的分库分表，也就是size必须是2的幂次方。因为哈希散列仅支持2的幂次方
     * 2.通过扰动函数进行哈希散列得出索引值
     * 3.实际分库分表后是一个二维数组，库是行，表是列。但是通过哈希散列计算出的下标是一维数组的，所以需要计算转换
     * 4.设置ThreadLocal中
     * @param dbKeyAttr
     */
    @Override
    public void dbRouter(String dbKeyAttr) {
        //长度 = 库数量 * 表数量
        int size = dbRouterConfig.getDbCount() * dbRouterConfig.getTbCount();
        //扰动函数：散列更加均匀
        int idx = (size - 1) & (dbKeyAttr.hashCode() ^ (dbKeyAttr.hashCode() >>> 16));
        //计算位置，将一维数组的下标位置(idx)转为二维数组位置
        int dbIdx = idx / dbRouterConfig.getTbCount() + 1;
        int tbIdx = idx - dbRouterConfig.getTbCount() * (dbIdx - 1);
        //设置到ThreadLocal
        DBContextHolder.setDbKey(String.format("%02d", dbIdx));
        DBContextHolder.setTbKey(String.format("%03d", tbIdx));
        logger.info("数据库路由 dbIdx：{} tbIdx：{}",  dbIdx, tbIdx);
    }

    @Override
    public void setDBKey(int dbIdx) {
        DBContextHolder.setDbKey(String.format("%02d", dbIdx));
    }

    @Override
    public void setTBKey(int tbIdx) {
        DBContextHolder.setTbKey(String.format("%03d", tbIdx));
    }

    @Override
    public int dbCount() {
        return dbRouterConfig.getDbCount();
    }

    @Override
    public int tbCount() {
        return dbRouterConfig.getTbCount();
    }

    @Override
    public void clear() {
        DBContextHolder.clearDbKey();
        DBContextHolder.clearTbKey();
    }
}
